﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Text;
using System.Threading.Tasks;
using Windows.ApplicationModel.Resources;
using Windows.Devices.Geolocation;
using Windows.Storage;
using BuddyTracker.Interfaces;
using BuddyTracker.ViewModel.Encoders;
using BuddyTracker.ViewModel.Helpers;
using CoreTweet;
using IServiceProvider = BuddyTracker.Interfaces.IServiceProvider;
using User = BuddyTracker.Model.User;

namespace BuddyTracker.ViewModel.Providers
{
    class TwitterServiceProvider : IServiceProvider
    {
        /// <summary>
        /// Current OAuthSession for twitter invoking.
        /// </summary>
        private OAuth.OAuthSession session;
        /// <summary>
        /// Current token with user credentials.
        /// </summary>
        private Tokens token = null;
        /// <summary>
        /// Follower's secret keys for generating tokens and sessions.
        /// </summary>
        private const string ConsumerKey = "gOgFQCnsI6ZpKX1fa1BJHhnQa";
        private const string ConsumerSecret = "mL2xbZ2STPpt1H43sB9zNrp6bH64fW89CJ4oxTVIkRqS6lmPLe";
        /// <summary>
        /// User's secret keys for generating tokens and sessions.
        /// </summary>
        private string AccessToken;
        private string AccessTokenSecret;
        /// <summary>
        /// Loader for dictionary.
        /// </summary>
        private ResourceLoader loader = new ResourceLoader();
        /// <summary>
        /// Logged in user screenname.
        /// </summary>
        private string _username;
        /// <summary>
        /// List of all location encoder for encoding decoding.
        /// </summary>
        private List<ILocationEncoder> listEncoders;
        /// <summary>
        /// How deep to look for our location strings.
        /// </summary>
        private const short LookupLimit = 50;
        /// <summary>
        /// How many posts per user?
        /// </summary>
        private const short LookupDepth = 1;

        public TwitterServiceProvider()
        {
            // was the user logged in?
            var localSettings = ApplicationData.Current.LocalSettings;
            if (localSettings.Values.ContainsKey("AccessToken") && localSettings.Values.ContainsKey("AccessTokenSecret"))
            {
                AccessToken = localSettings.Values["AccessToken"] as String;
                AccessTokenSecret = localSettings.Values["AccessTokenSecret"] as String;
                _username = localSettings.Values["ScreenName"] as String;

                // creating token immediately!
                token = Tokens.Create(ConsumerKey, ConsumerSecret, AccessToken, AccessTokenSecret);
            }

            // initialize list of encoders
            listEncoders = new List<ILocationEncoder>() { 
                new TextLocationEncoder(), new HexLocationEncoder(), new AESLocationEncoder()
            };

        }

        /// <summary>
        /// Return logged in users screen name or N/A if user isn't logged in.
        /// </summary>
        public string Username()
        {
            return _username != null ? _username : "N/A";
        }

        /// <summary>
        /// Get link for authorization.
        /// </summary>
        /// <returns>Autorization link.</returns>
        public async Task<Uri> Login() 
        {
            session = await OAuth.AuthorizeAsync(ConsumerKey, ConsumerSecret);
            var url = session.AuthorizeUri;
            var u = new Uri(url.OriginalString, UriKind.Absolute);            
            return u;
        }

        /// <summary>
        /// Returns is the user logged in. 
        /// </summary>
        public bool IsLoggedIn()
        {
            return token != null;
        }

        /// <summary>
        /// After user enters his PIN trying to get keys for making a token.
        /// </summary>
        /// <param name="PIN">User entered pin.</param>
        /// <returns>True if successful.</returns>
        public async Task<Boolean> Auth(string PIN)
        {
            try
            {
                token = await session.GetTokensAsync(PIN);

                AccessToken = token.AccessToken;
                AccessTokenSecret = token.AccessTokenSecret;
                _username = token.ScreenName;

                //saving tokens for future logins
                var localSettings = ApplicationData.Current.LocalSettings;
                localSettings.Values.Add("AccessToken", AccessToken);
                localSettings.Values.Add("AccessTokenSecret", AccessTokenSecret);
                localSettings.Values.Add("ScreenName", token.ScreenName);

                MessageDialogHelper.Show(loader.GetString("messageLoggedIn"), loader.GetString("Success"));

                return true;

            }
            catch (Exception ex)
            {
                MessageDialogHelper.ShowErrorMessage(ex.Message, ex);
                return false;
            }
        }

        /// <summary>
        /// Sends location to Twitter with supplied encoder.
        /// </summary>
        public async Task PostLocation(Geopoint location, ILocationEncoder locationEncoder)
        {
            if (token == null)
            {
                MessageDialogHelper.ShowErrorMessage(loader.GetString("ErrorNotLoggedIn"), new Exception("ErrorNotLoggedIn"));                
            }
            else
            {
                string loc = locationEncoder.EncodeLocation(location);
                await token.Statuses.UpdateAsync(status => loc);
                
            }
        }

        /// <summary>
        /// Logs off the user, deletes secret keys...
        /// </summary>
        public void LogOff()
        {
            var localSettings = ApplicationData.Current.LocalSettings;
            localSettings.Values.Remove("AccessToken");
            localSettings.Values.Remove("AccessTokenSecret");
            localSettings.Values.Remove("ScreenName");
            token = null;
            
            MessageDialogHelper.Show(loader.GetString("LogoffSuccess"), loader.GetString("Success"));          
        }

        /// <summary>
        /// Search for buddies and displays them on the map.
        /// </summary>
        /// <param name="buddies">List of buddies for search.</param>
        /// <param name="helper">Map containter.</param>
        public async Task Search(ObservableCollection<User> buddies, LocationHelper helper)
        {
            if (token == null)
            {
                MessageDialogHelper.ShowErrorMessage(loader.GetString("ErrorNotLoggedIn"), new Exception("No token"));
            }
            else
            {
                List<User> notFound = new List<User>();
                foreach (User buddy in buddies)
                {
                    try
                    {
                        var tweets = await token.Statuses.UserTimelineAsync(new Dictionary<string, object>()
                        {
                            {"screen_name", buddy.ScreenName},
                            {"count", LookupLimit} //annonymous type, searching through first 50 tweets
                        });

                        User u = null;

                        foreach (var tweet in tweets)
                        {
                            int foundHistory = 0;

                            //trying all decoders
                            foreach (ILocationEncoder locationEncoder in listEncoders)
                            {
                                var user = locationEncoder.DecodeString(tweet.Text);
                                if (user != null)
                                {
                                    u = user;
                                    foundHistory++;
                                    break;
                                }
                            }


                            if (u != null) //check one more time
                            {
                                //filling up
                                u.ScreenName = buddy.ScreenName;
                                helper.MarkBuddy(u);
                                buddy.LastLocation = u.LastLocation;
                                buddy.LastSeen = u.LastSeen;
                                buddy.ScreenName = u.ScreenName;
                            }
                            else
                            {
                                //didn't found buddy or didn't find suitable location string.
                            }

                            if (foundHistory >= LookupDepth)
                                break; //done
                        }
                    }
                    catch
                    {
                        notFound.Add(buddy);
                        //didn't find buddy, wrong username???
                    }
                }

                if (notFound.Count > 0)
                {
                    StringBuilder sb = new StringBuilder(loader.GetString("NotFound"));

                    foreach (User unf in notFound)
                    {
                        sb.Append(unf.ScreenName);
                        sb.Append(", ");
                    }

                    sb.Remove(sb.Length - 2, 2);
                    sb.Append(".");

                    MessageDialogHelper.Show(sb.ToString(), loader.GetString("Error"));
                }
                //helper.Zoom(); // TODO finally, adjust map zoom, not neccessary for now
            }
        }
    }
}
